package wordUtil.docExport;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.log4j.Logger;
import org.apache.poi.POIXMLDocument;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.openxml4j.opc.PackagePart;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.apache.poi.xwpf.usermodel.XWPFTableRow;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlToken;
import org.openxmlformats.schemas.drawingml.x2006.main.CTNonVisualDrawingProps;
import org.openxmlformats.schemas.drawingml.x2006.main.CTPositiveSize2D;
import org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.CTInline;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTColor;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTRPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTText;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTUnderline;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STFldCharType;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STUnderline;

import com.jfinal.kit.StrKit;
import com.jfinal.plugin.activerecord.Record;

/**
 * 自定义 XWPFDocument，并重写 createPicture()方法
 */
public class CustomXWPFDocument extends XWPFDocument {

	public CustomXWPFDocument(InputStream in) throws IOException {
		super(in);
	}

	public CustomXWPFDocument() {
		super();
	}

	public CustomXWPFDocument(OPCPackage pkg) throws IOException {
		super(pkg);
	}

	/**
	 * 根据指定的参数值、模板，生成 word 文档。模板只支持docx文档。变量名称只能是“字母”、“数字”及“英文句点”
	 * 
	 * @param param
	 *            需要替换的键值对，用value来替换key。vlaue为String时表示替换为文字，value为Pictrue时表示替换图片
	 * @param template
	 *            模板
	 * @throws IOException
	 */
	public static CustomXWPFDocument doc = null;

	public static CustomXWPFDocument generateWord(List<Record> dataList, String template) throws IOException {

		OPCPackage pack = POIXMLDocument.openPackage(template);
		doc = new CustomXWPFDocument(pack);
		if (dataList != null && dataList.size() > 0) {

			// 处理段落
			// List<XWPFParagraph> paragraphList = doc.getParagraphs();
			// processParagraphs(paragraphList, param, doc);

			// 处理表格
			Iterator<XWPFTable> it = doc.getTablesIterator();
			while (it.hasNext()) {
				XWPFTable table = it.next(); // 表

				List<XWPFTableRow> rows = table.getRows(); // 行
				for (int rowNum = 0; rowNum < rows.size(); rowNum++) {
					XWPFTableRow row = rows.get(rowNum);
					List<XWPFTableCell> cells = row.getTableCells(); // 每一行的所有列

					if (rowContainKey(row)) { // 这一行含有key_,则可能是重复行

						String firstKey = getRowContainKey(row);

						Record result = getGroupDataContain(dataList, firstKey);
						if (result != null) {
							int copyRowCount = copyTableRowAndFillData(table, rows, rowNum, result);
							logger.info(firstKey + " 复制行数 " + copyRowCount);
							rowNum = rowNum + copyRowCount;
							continue;
						}
					}

					// 赋值
					for (XWPFTableCell cell : cells) {
						List<XWPFParagraph> paragraphListTable = cell.getParagraphs();

						processParagraphs(paragraphListTable, dataList, doc);
					}
				}
			}
		}

		return doc;
	}

	private static String getRowContainKey(XWPFTableRow row) {
		List<XWPFTableCell> cells = row.getTableCells(); // 每一行的所有列

		for (int j = 0; j < cells.size(); j++) { // 列

			XWPFTableCell cell = cells.get(j);
			String text = cell.getText();
			if (StrKit.notBlank(text) && text.startsWith("key_") && !text.startsWith("key_xuhao")) {
				return text;
			}
		}
		return null;
	}

	private static Record getGroupDataContain(List<Record> dataList, String key) {

		for (Record result : dataList) {
			int isgroup = result.get("isgroup", 0);
			if (1 == isgroup) {
				@SuppressWarnings("unchecked")
				List<List<Record>> groupDataList = (List<List<Record>>) result.get("data"); // List<List<Result>>

				for (int j = 0; j < groupDataList.size(); j++) {

					List<Record> data = groupDataList.get(j);

					for (Record field : data) {
						String key_ = field.getStr("key_");
						if (StrKit.notBlank(key_) && key.equals(key_)) {
							return result;
						}
					}
				}
			}
		}
		return null;
	}

	private static boolean rowContainKey(XWPFTableRow row) {

		List<XWPFTableCell> cells = row.getTableCells(); // 每一行的所有列

		for (int j = 0; j < cells.size(); j++) { // 列

			XWPFTableCell cell = cells.get(j);
			String text = cell.getText();
			if (StrKit.notBlank(text) && text.startsWith("key_")) {
				return true;
			}
		}
		return false;
	}

	/**
	 * 复制行，并填充数据
	 * 
	 * @param table
	 * @param rows
	 * @param rowNum
	 * @param result
	 */
	@SuppressWarnings("unchecked")
	private static int copyTableRowAndFillData(XWPFTable table, List<XWPFTableRow> rows, int rowNum, Record result) {
		List<List<Record>> groupDataList = (List<List<Record>>) result.get("data"); // List<List<Result>>
		int copyRowCount = 0;

		// 行复制
		XWPFTableRow xwpfTableRow = null;
		for (int j = 1; j < groupDataList.size(); j++) {
			if (!groupDataList.isEmpty() && groupDataList.size() > 0) {

				xwpfTableRow = rows.get(rowNum);

				XWPFTableRow insertNewTableRow = table.insertNewTableRow(j + rowNum);

				copyTableRow(xwpfTableRow, insertNewTableRow);
				copyRowCount++;
			}
		}

		// 赋值
		for (int j = 0; j < groupDataList.size(); j++) {

			List<Record> data = groupDataList.get(j);

			Record numRecord = new Record(); // 解决序号的问题
			numRecord.set("key_", "key_xuhao");
			numRecord.set("value", j + 1 + "");
			data.add(numRecord);

			XWPFTableRow _xwpfTableRow = table.getRow(j + rowNum);
			List<XWPFTableCell> cells_ = _xwpfTableRow.getTableCells(); // 每一行的所有列
			for (XWPFTableCell cell : cells_) {
				List<XWPFParagraph> paragraphListTable = cell.getParagraphs();

				processParagraphs(paragraphListTable, data, doc);
			}
		}
		return copyRowCount;
	}

	/**
	 * 复制目标行的数据
	 * 
	 * @param sourceRow
	 * @param tagetRow
	 */
	private static void copyTableRow(XWPFTableRow sourceRow, XWPFTableRow tagetRow) {

		tagetRow.getCtRow().setTrPr(sourceRow.getCtRow().getTrPr()); // 复制行属性
		List<XWPFTableCell> cellList = sourceRow.getTableCells();
		if (null == cellList) {
			return;
		}
		// 添加列、复制列以及列中段落属性
		XWPFTableCell targetCell = null;
		for (int index = 0; index < cellList.size(); index++) {
			XWPFTableCell sourceCell = cellList.get(index);
			targetCell = tagetRow.addNewTableCell(); // 新增一个TableCell

			targetCell.getCTTc().setTcPr(sourceCell.getCTTc().getTcPr()); // 列属性

			targetCell.getParagraphs().get(0).getCTP().setPPr(sourceCell.getParagraphs().get(0).getCTP().getPPr()); // 段落属性

			String text1 = sourceCell.getText(); // TableCell的文本内容
			List<XWPFParagraph> paragraphs = targetCell.getParagraphs();
			for (XWPFParagraph paragraph : paragraphs) {
				List<XWPFRun> runs = paragraph.getRuns(); // 带样式的文本
				if (runs != null && runs.size() == 0) {
					XWPFRun createRun = paragraph.createRun();
					List<XWPFRun> runs2 = sourceCell.getParagraphs().get(0).getRuns();
					if (runs2 != null && runs2.size() > 0) {
						createRun.getCTR().setRPr(runs2.get(0).getCTR().getRPr()); // run的属性
					}
					createRun.setText(text1, 0);

					paragraph.addRun(createRun);
				}
			}
		}
	}

	public static Logger logger = Logger.getLogger("xx");

	/**
	 * 是否包含汉字
	 * 
	 * @param str
	 * @return
	 */
	public static boolean isContainChinese(String str) {

		Pattern p = Pattern.compile("[\u4e00-\u9fa5]");
		Matcher m = p.matcher(str);
		if (m.find()) {
			return true;
		}
		return false;
	}

	/**
	 * 处理段落
	 * 
	 * @param paragraphList
	 */
	public static void processParagraphs(List<XWPFParagraph> paragraphList, List<Record> param, CustomXWPFDocument doc) {
		if (paragraphList != null && paragraphList.size() > 0) {
			for (XWPFParagraph paragraph : paragraphList) {
				// logger.info(paragraph.getText());

				List<XWPFRun> runs = paragraph.getRuns();
				for (XWPFRun run : runs) {
					String text = run.getText(0);

					// logger.info(text);
					if (text != null) {

						if (!isContainChinese(text)) { // 不包含中文：1.key不包含 2.字段本身不包含

							Object value = null;

							for (Record result : param) { // 遍历取值
								String key = result.getStr("key_");
								Object vv = result.get("value");
								if (StrKit.notBlank(key) && key.equals(text)) {
									value = vv;
								}
							}

							logger.info(text + " - " + value);

							if (value == null) {

								value = "";
								// continue;
							}

							run.setText("", 0);
							if (value instanceof String) {// 文本替换
								String dest = (String) value;
								if (dest.endsWith("jpg")) {

									// 图片加入超连接

									addParagraphTextHyperlink(paragraph, dest.replace(".jpg", ""), dest);

								} else {

									handleRunTextReplace(run, dest);
									// run.setText(dest,0);
								}

							} else if (value instanceof Pictrue) {// 图片替换

								Pictrue pic = (Pictrue) value;
								try {

									int ind = doc.addPicture(new ByteArrayInputStream(pic.getData()), pic.getType());

									doc.createPicture(ind, pic.getWidth(), pic.getHeight(), run);

								} catch (Exception e) {

									e.printStackTrace();
								}
							}
						}
					} else {
						run.setText("", 0);
						handleRunTextReplace(run, "");
					}
				}
			}
		}
	}

	/*------------------------------------Word 插入超链接---------------------------------------------------  */
	/**
	 * @Description: 默认的超链接样式
	 */
	public void addParagraphTextHyperlinkBasicStyle(XWPFParagraph paragraph, String url, String text, String fontFamily, String fontSize, String colorVal, boolean isBlod, boolean isItalic, boolean isStrike) {
		// addParagraphTextHyperlink(paragraph, url, text, fontFamily, fontSize, colorVal, isBlod, true, "0000FF", STUnderline.SINGLE, isItalic, isStrike, false, false, false, false, false, false, false, null, false, null, false, null, null, null, 0, 0, 0);
		addParagraphTextHyperlink(paragraph, url, text);
	}

	/**
	 * @Description: 设置超链接样式
	 */
	public static void addParagraphTextHyperlink(XWPFParagraph paragraph, String url, String text) {

		// String id = doc.getPackagePart().addExternalRelationship(url, XWPFRelation.HYPERLINK.getRelation()).getId();
		// // Append the link and bind it to the relationship
		// CTHyperlink cLink = paragraph.getCTP().addNewHyperlink();
		// cLink.setId(id);

		// Add the link as External relationship
		XWPFDocument document = paragraph.getDocument();

		document = doc;
		if (document != null) {
			PackagePart packagePart = document.getPackagePart();
			if (packagePart != null) {

				// 插入当前时间
				// XWPFRun run = paragraph.createRun();
				// paragraph.getCTP().addNewFldSimple().setInstr("DATE \\@ \"yyyy-MM-dd\" \\* MERGEFORMAT");

				// TODO 如何跳转至指定的书签
				// CTP ctP = paragraph.getCTP();
				// CTSimpleField toc = ctP.addNewFldSimple();
				// toc.setInstr("HYPERLINK \\l " + url);
				// toc.setDirty(STOnOff.TRUE);

				// paragraph.getRuns().get(0).setText(url);

				XWPFRun runBegin = paragraph.createRun(); // 可点击区域：开始
				runBegin.getCTR().addNewFldChar().setFldCharType(STFldCharType.BEGIN);

				XWPFRun runTo = paragraph.createRun(); // 点击需要调整的目标书签
				CTText addNewInstrText = runTo.getCTR().addNewInstrText();
				/*** 模拟在word中插入域的方式，可以扩展：插入当前时间、跳转至本地某文件、自动序列号等、插入图片等 **/

				// builder.insertImage(ImageIO.read(new File("E:\\vm\\模板\\out\\20170505160052.jpg")));
				// 插入图片 的域代码:"INCLUDEPICTURE "d:\\kingsoft.jpg"
				addNewInstrText.setStringValue("HYPERLINK \\l " + url);
				// addNewInstrText.setStringValue("INCLUDEPICTURE E:\\vm\\模板\\out\\20170505160052.jpg");

				XWPFRun runSeparate = paragraph.createRun(); // 间隙
				runSeparate.getCTR().addNewFldChar().setFldCharType(STFldCharType.SEPARATE);

				XWPFRun runText = paragraph.createRun(); // 文本区域
				CTRPr newRPr = runText.getCTR().addNewRPr();

				// 字体颜色
				CTColor color = newRPr.addNewColor();
				color.setVal("0000FF");
				newRPr.setColor(color);

				// 下划线
				CTUnderline udLine = newRPr.addNewU();
				udLine.setVal(STUnderline.SINGLE);
				udLine.setColor("0000FF");

				runText.setText(url);

				XWPFRun runEnd = paragraph.createRun(); // 可点击区域：结束
				runEnd.getCTR().addNewFldChar().setFldCharType(STFldCharType.END);
			}
		}
	}

	/**
	 * 文字替换，处理了回车符不能显示的问题
	 * 
	 * @param run
	 * @param dest
	 */
	@SuppressWarnings("deprecation")
	private static void handleRunTextReplace(XWPFRun run, String dest) {
		String[] ps = dest.split("\n"); // 处理回车

		run.setText(ps[0], 0);

		for (int i = 1; i < ps.length; i++) {

			run.getCTR().addNewBr(); // 替换回车符
			run.getCTR().addNewInstrText().set(ps[i]);
		}
	}

	/**
	 * @param id
	 * @param width
	 *            宽
	 * @param height
	 *            高
	 * @param run
	 *            当前的位置
	 */
	public void createPicture(int id, int width, int height, XWPFRun run) {
		final int EMU = 9525;
		width *= EMU;
		height *= EMU;
		String blipId = getAllPictures().get(id).getPackageRelationship().getId();
		CTInline inline = run.getCTR().addNewDrawing().addNewInline();
		String picXml = "" + "<a:graphic xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\">" + "   <a:graphicData uri=\"http://schemas.openxmlformats.org/drawingml/2006/picture\">" + "      <pic:pic xmlns:pic=\"http://schemas.openxmlformats.org/drawingml/2006/picture\">" + "         <pic:nvPicPr>" + "            <pic:cNvPr id=\"" + id + "\" name=\"Generated\"/>" + "            <pic:cNvPicPr/>" + "         </pic:nvPicPr>" + "         <pic:blipFill>" + "            <a:blip r:embed=\"" + blipId + "\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\"/>" + "            <a:stretch>" + "               <a:fillRect/>" + "            </a:stretch>" + "         </pic:blipFill>" + "         <pic:spPr>" + "            <a:xfrm>"
				+ "               <a:off x=\"0\" y=\"0\"/>" + "               <a:ext cx=\"" + width + "\" cy=\"" + height + "\"/>" + "            </a:xfrm>" + "            <a:prstGeom prst=\"rect\">" + "               <a:avLst/>" + "            </a:prstGeom>" + "         </pic:spPr>" + "      </pic:pic>" + "   </a:graphicData>" + "</a:graphic>";

		inline.addNewGraphic().addNewGraphicData();
		XmlToken xmlToken = null;
		try {
			xmlToken = XmlToken.Factory.parse(picXml);
		} catch (XmlException xe) {
			xe.printStackTrace();
		}
		inline.set(xmlToken);

		inline.setDistT(0);
		inline.setDistB(0);
		inline.setDistL(0);
		inline.setDistR(0);

		CTPositiveSize2D extent = inline.addNewExtent();
		extent.setCx(width);
		extent.setCy(height);

		CTNonVisualDrawingProps docPr = inline.addNewDocPr();
		docPr.setId(id);
		docPr.setName("pic" + id);
		docPr.setDescr("description");
	}
}